package org.openmrs.module.simplelabentry.web.controller;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.openmrs.Concept;
import org.openmrs.ConceptAnswer;
import org.openmrs.ConceptNumeric;
import org.openmrs.ConceptSet;
import org.openmrs.Location;
import org.openmrs.Order;
import org.openmrs.Patient;
import org.openmrs.api.OrderService.ORDER_STATUS;
import org.openmrs.api.context.Context;
import org.openmrs.module.hospitalcore.model.Lab;
import org.openmrs.module.simplelabentry.SimpleLabEntryService;
import org.openmrs.module.simplelabentry.web.dwr.LabOrderListItem;
import org.openmrs.module.simplelabentry.web.dwr.LabResultListItem;
import org.openmrs.module.simplelabentry.web.dwr.Node;
import org.openmrs.web.controller.PortletController;
import org.springframework.util.StringUtils;

public class PrintPatientReportPortletController extends PortletController {

	protected final Log log = LogFactory.getLog(getClass());

	@SuppressWarnings("unchecked")
	protected void populateModel(HttpServletRequest request, Map model) {
		SimpleLabEntryService ls = (SimpleLabEntryService) Context
				.getService(SimpleLabEntryService.class);
		Lab lab = ls.getCurrentLab();
		String orderDateStr = request.getParameter("orderDate");
		String identifier = request.getParameter("identifier");
		String limit = (String) model.get("limit");

		String orderLocationId = "1";
		List<Order> labOrderList = new ArrayList<Order>();
		List<LabOrderListItem> labOrderListItemList = new ArrayList<LabOrderListItem>();
		List<ConceptNumeric> conceptNumericList = new ArrayList<ConceptNumeric>();
		try {
			Location location = StringUtils.hasText(orderLocationId) ? Context
					.getLocationService().getLocation(
							Integer.parseInt(orderLocationId)) : null;
			Date orderDate = StringUtils.hasText(orderDateStr) ? Context
					.getDateFormat().parse(orderDateStr) : null;
			ORDER_STATUS status = "open".equals(limit) ? ORDER_STATUS.CURRENT
					: "closed".equals(limit) ? ORDER_STATUS.COMPLETE
							: ORDER_STATUS.NOTVOIDED;
			List<Patient> patients = null;
			List<Concept> allTestsList = new ArrayList<Concept>();
			boolean check = true;
			String prefix = Context.getAdministrationService()
					.getGlobalProperty("registration.identifier_prefix");
			if (identifier.contains("-") && !identifier.contains(prefix)) {
				identifier = prefix + identifier;
			}
			patients = Context.getPatientService().getPatients(identifier);
			// Retrieve matching orders
			if (patients != null && patients.size() > 1) {
				model.put("listPatients", patients);
			} else if (patients != null && patients.size() == 1) {
				List<Order> orderList = ls
						.getLabOrders(null, location, orderDate, status,
								patients, lab.getInvestigationsToDisplay(),
								lab.getLabOrderType());
				for (Order order : orderList) {
					labOrderList.add(order);
					LabOrderListItem labOrderListItem = new LabOrderListItem(
							order);
					labOrderListItemList.add(labOrderListItem);
					// System.out.println("order = " +
					// order.getConcept().getName() +
					// "startdate = "+order.getStartDate() +
					// " id = "+order.getId());
				}

				model.put("labOrderListItems", labOrderListItemList);
				model.put("labOrders", labOrderList);
				model.put("patient", patients.get(0));
				
				try {
					List<Node> nodes = new ArrayList<Node>();
					
					SimpleLabEntryService labEntryService = Context.getService(SimpleLabEntryService.class);
					Lab laborary = labEntryService.getCurrentLab();					
					Set<Concept> in = laborary.getInvestigationsToDisplay();
					
					List<Node> finalNodes = new ArrayList<Node>();
					for(Concept c:in){
						List<Node> tempNodes = new ArrayList<Node>();
						buildTree(tempNodes, c, 1, null, labOrderListItemList);						
						List<Node> tempFinalNodes = new ArrayList<Node>();
						printNode(tempFinalNodes, tempNodes.get(0));
						finalNodes.addAll(tempFinalNodes);
					}
					//printNode(finalNodes, nodes.get(0));
					//finalNodes.remove(0);
					model.put("nodes", finalNodes);
				} catch (Exception e) {
					System.out.println(" Building tree ERROR!!! ");
					e.printStackTrace();
				}
			}

		} catch (Exception e) {
			throw new RuntimeException(
					"Server Error: Unable to find patient report.", e);
		}

	}

	// Build the hierarchical of all test/labset
	private void buildTree(List<Node> nodes, Concept concept, Integer level,
			Node parent, List<LabOrderListItem> labOrderListItemList) {
		if ((concept != null)
				&& (!concept.getConceptClass().getName().equals("Finding"))) {
			Node node = new Node(level, concept, parent);
			
			node.setName(concept.getName().getName());
			if(!org.apache.commons.lang.StringUtils.isBlank(concept.getName().getShortName())){
				node.setName(concept.getName().getShortName());
			} else {
				node.setName(concept.getName().getName());
			}
			
			nodes.add(node);
			if ((concept.getConceptClass().getName().equals("Test"))
					|| (concept.getConceptClass().getName().equals("LabSet"))) {
				List<LabOrderListItem> items = findLabOrderListItem(
						labOrderListItemList, concept);
				if (!CollectionUtils.isEmpty(items)) {
					node.setItems(items);
					updateChildNode(node);
					setValueTest(node);
					setValueLabSet(node);
				}
			} else {
				for (ConceptAnswer ca : concept.getAnswers()) {
					Concept c = ca.getAnswerConcept();
					buildTree(nodes, c, level + 1, node, labOrderListItemList);
				}

				for (ConceptSet cs : concept.getConceptSets()) {
					Concept c = cs.getConcept();
					buildTree(nodes, c, level + 1, node, labOrderListItemList);
				}
			}

		} else {
			System.out.println("Null concept");
		}
	}

	// Recursive method to add a child to a parent node
	private void updateChildNode(Node node) {

		if (node != null) {
			if (node.getParent() != null) {
				node.getParent().getChildNodes().add(node);
				updateChildNode(node.getParent());
			}
		}
	}

	// Printout all nodes to the list. Those nodes should be rendered on view
	private void printNode(List<Node> nodes, Node node) {
		if (node != null) {
			nodes.add(node);
			for (Node child : node.getSortedChildNodes()) {
				if ((!CollectionUtils.isEmpty(child.getChildNodes())
						|| (child.getConcept().getConceptClass().getName()
								.equals("Test")) || (child.getConcept()
						.getConceptClass().getName().equals("LabSet"))))
					printNode(nodes, child);
			}
		}
	}	
	
	// Set value for test
	private void setValueTest(Node node){
		if(node.getConcept().getConceptClass().getName().equals("Test")){
			if(!CollectionUtils.isEmpty(node.getItems())){
				LabOrderListItem item = node.getItems().get(0);
				if(!CollectionUtils.isEmpty(item.getLabResults())){
					LabResultListItem result = item.getLabResults().get(0);
					node.setLowNormal(result.getLowNormal());
					node.setHighNormal(result.getHighNormal());
					node.setResult(result.getResult());
					node.setUnit(result.getUnit());
				}					
			}
		}
	}
	
	// Set value labset
	private void setValueLabSet(Node node){
		if(node.getConcept().getConceptClass().getName().equals("LabSet")) {
			if(!CollectionUtils.isEmpty(node.getItems())){	
				for(LabOrderListItem item:node.getItems()){
					if(!CollectionUtils.isEmpty(item.getLabResults())){
						for(LabResultListItem result:item.getLabResults()){								
							Node child = new Node(node.getLevel()+1, node.getConcept(), node);
							child.setName(result.getConceptName());
							child.setLowNormal(result.getLowNormal());
							child.setHighNormal(result.getHighNormal());
							child.setResult(result.getResult());
							child.setUnit(result.getUnit());
							node.getChildNodes().add(child);
						}							
					}
				}										
			}
		}
	}

	// Search for the labOrderListItem that matches the concept
	private List<LabOrderListItem> findLabOrderListItem(
			List<LabOrderListItem> list, Concept concept) {
		List<LabOrderListItem> ls = new ArrayList<LabOrderListItem>();

		for (LabOrderListItem item : list) {
			if (item.getConceptId().equals(concept.getId()))
				ls.add(item);
		}

		return ls;
	}

}
